home *** CD-ROM | disk | FTP | other *** search
/ Collection of Tools & Utilities / Collection of Tools and Utilities.iso / edit / elv18src.zip / ctags.c < prev    next >
C/C++ Source or Header  |  1994-01-17  |  21KB  |  908 lines

  1. /* ctags.c */
  2.  
  3. /* This is a reimplementation of the ctags(1) program.  It supports ANSI C,
  4.  * and has heaps o' flags.  It is meant to be distributed with elvis.
  5.  */
  6.  
  7. #include <stdio.h>
  8. #ifdef __STDC__
  9. # include <string.h>
  10. # include <stdlib.h>
  11. #endif
  12. #include "config.h"
  13. #ifndef FALSE
  14. # define FALSE    0
  15. # define TRUE    1
  16. #endif
  17. #ifndef TAGS
  18. # define TAGS    "tags"
  19. #endif
  20. #ifndef REFS
  21. # define REFS    "refs"
  22. #endif
  23. #ifndef BLKSIZE
  24. # define BLKSIZE 1024
  25. #endif
  26.  
  27. #include "ctype.c" /* yes, that really is the .c file, not the .h one. */
  28.  
  29. extern void    file_open P_((char *));
  30. extern int    file_getc P_((void));
  31. extern void    file_ungetc P_((int));
  32. extern void    file_copyline P_((long, FILE *));
  33. extern void    cpp_open P_((char *));
  34. extern void    cpp_echo P_((int));
  35. extern int    cpp_getc P_((void));
  36. extern void    cpp_ungetc P_((int));
  37. extern int    lex_gettoken P_((void));
  38. extern void    maketag P_((int, long));
  39. extern void    ctags P_((char *));
  40. extern void    usage P_((void));
  41. extern void    main P_((int, char **));
  42.  
  43.  
  44. /* -------------------------------------------------------------------------- */
  45. /* Some global variables */
  46.  
  47. /* The following boolean variables are set according to command line flags */
  48. int    backward;    /* -B  regexp patterns search backwards */
  49. int    no_colons;    /* -S  make static tags look like global tags */
  50. int    incl_static;    /* -s  include static tags */
  51. int    incl_types;    /* -t  include typedefs and structs */
  52. int    incl_vars;    /* -v  include variables */
  53. int    make_refs;    /* -r  generate a "refs" file */
  54. int    append_files;    /* -a  append to "tags" [and "refs"] files */
  55. #ifdef DEVNULL
  56. int    dont_make_tags;    /* -T  Don't make \"tags\" */
  57. #endif
  58.  
  59. /* The following are used for outputting to the "tags" and "refs" files */
  60. FILE    *tags;        /* used for writing to the "tags" file */
  61. FILE    *refs;        /* used for writing to the "refs" file */
  62.  
  63. /* -------------------------------------------------------------------------- */
  64. /* These are used for reading a source file.  It keeps track of line numbers */
  65. char    *file_name;    /* name of the current file */
  66. FILE    *file_fp;    /* stream used for reading the file */
  67. long    file_lnum;    /* line number in the current file */
  68. long    file_seek;    /* fseek() offset to the start of current line */
  69. int    file_afternl;    /* boolean: was previous character a newline? */
  70. int    file_prevch;    /* a single character that was ungotten */
  71. int    file_header;    /* boolean: is the current file a header file? */
  72.  
  73. /* This function opens a file, and resets the line counter.  If it fails, it
  74.  * it will display an error message and leave the file_fp set to NULL.
  75.  */
  76. void file_open(name)
  77.     char    *name;    /* name of file to be opened */
  78. {
  79.     /* if another file was already open, then close it */
  80.     if (file_fp)
  81.     {
  82.         fclose(file_fp);
  83.     }
  84.  
  85.     /* try to open the file for reading.  The file must be opened in
  86.      * "binary" mode because otherwise fseek() would misbehave under DOS.
  87.      */
  88. #if MSDOS || TOS || OS2
  89.     file_fp = fopen(name, "rb");
  90. #else
  91.     file_fp = fopen(name, "r");
  92. #endif
  93.     if (!file_fp)
  94.     {
  95.         perror(name);
  96.     }
  97.  
  98.     /* reset the name & line number */
  99.     file_name = name;
  100.     file_lnum = 0L;
  101.     file_seek = 0L;
  102.     file_afternl = TRUE;
  103.  
  104.     /* determine whether this is a header file */
  105.     file_header = FALSE;
  106.     name += strlen(name) - 2;
  107.     if (name >= file_name && name[0] == '.' && (name[1] == 'h' || name[1] == 'H'))
  108.     {
  109.         file_header = TRUE;
  110.     }
  111. }
  112.  
  113. /* This function reads a single character from the stream.  If the *previous*
  114.  * character was a newline, then it also increments file_lnum and sets
  115.  * file_offset.
  116.  */
  117. int file_getc()
  118. {
  119.     int    ch;
  120.  
  121.     /* if there is an ungotten character, then return it.  Don't do any
  122.      * other processing on it, though, because we already did that the
  123.      * first time it was read.
  124.      */
  125.     if (file_prevch)
  126.     {
  127.         ch = file_prevch;
  128.         file_prevch = 0;
  129.         return ch;
  130.     }
  131.  
  132.     /* if previous character was a newline, then we're starting a line */
  133.     if (file_afternl && file_fp)
  134.     {
  135.         file_afternl = FALSE;
  136.         file_seek = ftell(file_fp);
  137.         file_lnum++;
  138.     }
  139.  
  140.     /* Get a character.  If no file is open, then return EOF */
  141.     ch = (file_fp ? getc(file_fp) : EOF);
  142.  
  143.     /* if it is a newline, then remember that fact */
  144.     if (ch == '\n')
  145.     {
  146.         file_afternl = TRUE;
  147.     }
  148.  
  149.     /* return the character */
  150.     return ch;
  151. }
  152.  
  153. /* This function ungets a character from the current source file */
  154. void file_ungetc(ch)
  155.     int    ch;    /* character to be ungotten */
  156. {
  157.     file_prevch = ch;
  158. }
  159.  
  160. /* This function copies the current line out some other fp.  It has no effect
  161.  * on the file_getc() function.  During copying, any '\' characters are doubled
  162.  * and a leading '^' or trailing '$' is also quoted.  The '\n' character is not
  163.  * copied.  If the '\n' is preceded by a '\r', then the '\r' isn't copied.
  164.  *
  165.  * This is meant to be used when generating a tag line.
  166.  */
  167. void file_copyline(seek, fp)
  168.     long    seek;    /* where the lines starts in the source file */
  169.     FILE    *fp;    /* the output stream to copy it to */
  170. {
  171.     long    oldseek;/* where the file's pointer was before we messed it up */
  172.     char    ch;    /* a single character from the file */
  173.     char    next;    /* the next character from this file */
  174.  
  175.     /* go to the start of the line */
  176.     oldseek = ftell(file_fp);
  177.     fseek(file_fp, seek, 0);
  178.  
  179.     /* if first character is '^', then quote it */
  180.     ch = getc(file_fp);
  181. #if 0
  182.     if (ch == '^')
  183.     {
  184.         putc('\\', fp);
  185.     }
  186. #endif
  187.  
  188.     /* write everything up to, but not including, the newline */
  189.     while (ch != '\n')
  190.     {
  191.         /* preread the next character from this file */
  192.         next = getc(file_fp);
  193.  
  194.         /* if character is '\', or a terminal '$', then quote it */
  195.         if (ch == '\\'
  196.          || ch == (backward ? '?' : '/')
  197.          || (ch == '$' && next == '\n'))
  198.         {
  199.             putc('\\', fp);
  200.         }
  201.  
  202.         /* copy the character, unless it is a terminal '\r' */
  203.         if (ch != '\r' || next != '\n')
  204.             putc(ch, fp);
  205.  
  206.         /* next character... */
  207.         ch = next;
  208.     }
  209.  
  210.     /* seek back to the old position */
  211.     fseek(file_fp, oldseek, 0);
  212. }
  213.  
  214. /* -------------------------------------------------------------------------- */
  215. /* This section handles preprocessor directives.  It strips out all of the
  216.  * directives, and may emit a tag for #define directives.
  217.  */
  218.  
  219. int    cpp_afternl;    /* boolean: look for '#' character? */
  220. int    cpp_prevch;    /* an ungotten character, if any */
  221. int    cpp_refsok;    /* boolean: can we echo characters out to "refs"? */
  222. int    cpp_refsnl;    /* boolean: dup next \n in "refs"? */
  223.  
  224. /* This function opens the file & resets variables */
  225. void cpp_open(name)
  226.     char    *name;    /* name of source file to be opened */
  227. {
  228.     /* use the lower-level file_open function to open the file */
  229.     file_open(name);
  230.  
  231.     /* reset variables */
  232.     cpp_afternl = TRUE;
  233.     cpp_refsok = TRUE;
  234. }
  235.  
  236. /* This function copies a character from the source file to the "refs" file */
  237. void cpp_echo(ch)
  238.     int    ch; /* the character to copy */
  239. {
  240.     static    wasnl;
  241.  
  242.     /* echo non-EOF chars, unless not making "ref", or echo turned off */
  243.     if (ch != EOF && make_refs && cpp_refsok && !file_header)
  244.     {
  245.         /* try to avoid blank lines */
  246.         if (ch == '\n')
  247.         {
  248.             /* hack: double \n at end of declarations, to
  249.                help `ref' find the right starting point...
  250.             */
  251.             if (cpp_refsnl)
  252.             {
  253.                 putc('\n', refs);
  254.                 cpp_refsnl = FALSE;
  255.             }
  256.             if (wasnl)
  257.             {
  258.                 return;
  259.             }
  260.             wasnl = TRUE;
  261.         }
  262.         else
  263.         {
  264.             wasnl = FALSE;
  265.         }
  266.  
  267.         /* add the character */
  268.         putc(ch, refs);
  269.     }
  270. }
  271.  
  272. /* This function returns the next character which isn't part of a directive */
  273. int cpp_getc()
  274. {
  275.     static
  276.     int    ch;    /* the next input character */
  277.     char    *scan;
  278.  
  279.     /* if we have an ungotten character, then return it */
  280.     if (cpp_prevch)
  281.     {
  282.         ch = cpp_prevch;
  283.         cpp_prevch = 0;
  284.         return ch;
  285.     }
  286.  
  287.     /* Get a character from the file.  Return it if not special '#' */
  288.     ch = file_getc();
  289.     if (ch == '\n')
  290.     {
  291.         cpp_afternl = TRUE;
  292.         cpp_echo(ch);
  293.         return ch;
  294.     }
  295.     else if (ch != '#' || !cpp_afternl)
  296.     {
  297.         /* normal character.  Any non-whitespace should turn off afternl */
  298.         if (ch != ' ' && ch != '\t')
  299.         {
  300.             cpp_afternl = FALSE;
  301.         }
  302.         cpp_echo(ch);
  303.         return ch;
  304.     }
  305.  
  306.     /* Yikes!  We found a directive */
  307.  
  308.     /* see whether this is a #define line */
  309.     scan = " define ";
  310.     while (*scan)
  311.     {
  312.         if (*scan == ' ')
  313.         {
  314.             /* space character matches any whitespace */
  315.             do
  316.             {
  317.                 ch = file_getc();
  318.             } while (ch == ' ' || ch == '\t');
  319.             file_ungetc(ch);
  320.         }
  321.         else
  322.         {
  323.             /* other characters should match exactly */
  324.             ch = file_getc();
  325.             if (ch !=